package util

import (
	"bytes"
	"crypto/md5"
	"encoding/binary"
	"encoding/hex"
	"fmt"
	"io"
	"net"
	"os"
	"path/filepath"
	"reflect"
	"runtime/debug"
	"sync"

	"yunj/pkg/global"
)

// 将 IPv4 地址字符串转换为 uint32
func IpToUint32(ipStr string) (uint32, error) {
	ip := net.ParseIP(ipStr)
	if ip == nil {
		return 0, fmt.Errorf("无效IP地址: %s", ipStr)
	}

	ipv4 := ip.To4()
	if ipv4 == nil {
		return 0, fmt.Errorf("非IPV4地址: %s", ipStr)
	}
	// 将 IPv4 地址的四个字节转换为 uint32
	return binary.BigEndian.Uint32(ipv4), nil
}

// 计算给定字符串的 MD5 哈希值，并返回其十六进制表示。
func Md5(s string) string {
	// 创建一个新的 MD5 哈希器
	hasher := md5.New()
	// 将字符串的字节写入哈希器
	hasher.Write([]byte(s))
	// 计算哈希值，得到字节切片
	hashBytes := hasher.Sum(nil)
	// 将哈希值字节切片转换为十六进制字符串
	hashString := hex.EncodeToString(hashBytes)
	return hashString
}

// 获取数据库表名
func TableName(model any) string {
	if tabler, ok := model.(interface{ TableName() string }); ok {
		return tabler.TableName()
	}

	t := reflect.TypeOf(model)
	// 如果传入的是指针，获取指针指向的值
	if t.Kind() == reflect.Ptr {
		t = t.Elem()
	}
	return global.Config.Database.TablePrefix + UppercaseToUnderline(t.Name())
}

// 获取实例类型名称的函数
func TypeName(instance any) string {
	if instance == nil {
		return ""
	}
	// 获取实例的反射类型
	typ := reflect.TypeOf(instance)
	// 如果传入的是指针，获取指针指向的类型
	if typ.Kind() == reflect.Ptr {
		typ = typ.Elem()
	}
	// 返回类型的名称
	return typ.Name()
}

// 获取实例的完整包名
func PackageFullName(instance any) string {
	// 获取实例的反射类型
	instanceType := reflect.TypeOf(instance)
	// 如果传入的是指针，需要获取指针指向的实际类型
	if instanceType.Kind() == reflect.Ptr {
		instanceType = instanceType.Elem()
	}
	return instanceType.PkgPath()
}

// 格式化错误信息
// Deprecated:方法已弃用，替代方法：yunj/pkg/yunj/core/log.ErrorStack()
func FormatError(err error, prefix ...string) string {
	if err == nil {
		return ""
	}
	stack := debug.Stack()
	var buf bytes.Buffer
	prefixStr := ""
	for _, s := range prefix {
		if s != "" {
			if prefixStr != "" {
				s = "=>" + s
			}
			prefixStr += s
		}
	}
	if prefixStr != "" {
		prefixStr += "\r\n"
	}
	buf.WriteString(prefixStr + err.Error() + "\r\nStack:")

	// 按行处理堆栈信息
	row := 1
	for i, line := range bytes.Split(stack, []byte{'\n'}) {
		if i <= 4 {
			continue
		}
		buf.WriteString("\r\n")
		buf.Write(line)
		// buf.WriteByte('\n')

		// if i == 0 {
		// 	// 第一行通常是 goroutine 信息
		// 	buf.WriteString(string(line))
		// 	buf.WriteByte('\n')
		// } else if len(line) > 0 {
		// 	// 为非空行添加缩进
		// 	buf.WriteString("  ")
		// 	buf.Write(line)
		// 	buf.WriteByte('\n')
		// }
		row++
	}

	return buf.String()
}

// 可比较类型缓存读写锁
var comparableTypeCacheMu sync.RWMutex
var comparableTypeCache = make(map[reflect.Type]bool)

// 检查类型是否可比较（带缓存）
func IsComparable(v interface{}) bool {
	if v == nil {
		return false
	}

	// 获取反射类型（处理指针）
	t := reflect.TypeOf(v)
	if t.Kind() == reflect.Ptr {
		t = t.Elem()
	}

	// 先读取缓存（无需锁）
	comparableTypeCacheMu.RLock()
	result, exists := comparableTypeCache[t]
	comparableTypeCacheMu.RUnlock()

	if exists {
		return result
	}

	// 写入缓存（加锁）
	comparableTypeCacheMu.Lock()
	defer comparableTypeCacheMu.Unlock()

	// 双重检查，避免并发写入
	if result, exists = comparableTypeCache[t]; exists {
		return result
	}

	// 计算并缓存结果
	result = t.Comparable()
	comparableTypeCache[t] = result
	return result
}

var areaOptions = make(map[string]map[string]string)

// 获取area可选地区数据内容
func GetAreaOptions() *map[string]map[string]string {
	if areaOptions != nil {
		return &areaOptions
	}
	// 获取当前工作目录
	rootDir, err := os.Getwd()
	if err != nil {
		panic(fmt.Sprintf("获取当前工作目录失败: %v\r\n", err))
	}
	// 打开文件
	filePath := "pkg/yunj/resource/static/yunj/json/area.json"
	file, err := os.Open(filepath.Join(rootDir, filePath))
	if err != nil {
		if os.IsNotExist(err) {
			panic(fmt.Sprintf("%s 文件未找到", filePath))
		} else {
			panic(fmt.Sprintf("%s 文件打开出错：%v", filePath, err))
		}
	}
	defer file.Close()
	// 读取文件内容
	data, err := io.ReadAll(file)
	if err != nil {
		panic(fmt.Sprintf("%s 文件读取出错：%v", filePath, err))
	}
	areaOptions, err = JsonTo[map[string]map[string]string](string(data))
	if err != nil {
		panic(fmt.Sprintf("%s 文件内容解析出错：%v", filePath, err))
	}
	return &areaOptions
}

// 获取毫秒耗时描述
func LatencyDescByMs(msTime uint64) string {
	if msTime < 1000 {
		return fmt.Sprintf("%d毫秒", msTime)
	}
	if msTime < 60000 {
		sNum := msTime / 1000
		msNum := msTime % 1000
		return fmt.Sprintf("%d秒%d毫秒", sNum, msNum)
	}
	if msTime < 3600000 {
		mNum := msTime / 60000
		sNum := (msTime % 60000) / 1000
		return fmt.Sprintf("%d分%d秒", mNum, sNum)
	}
	if msTime < 86400000 {
		hNum := msTime / 3600000
		mNum := (msTime % 3600000) / 60000
		return fmt.Sprintf("%d小时%d分", hNum, mNum)
	}
	dNum := msTime / 86400000
	hNum := (msTime % 86400000) / 3600000
	return fmt.Sprintf("%d天%d小时", dNum, hNum)
}
